package conn

import (
	"errors"
	"sync"
	"sync/atomic"

	"engine/log"
	"gitee.com/ameise84/e3net"
	"gitee.com/ameise84/e3utils/str_conv"
	"github.com/gorilla/websocket"
	"google.golang.org/protobuf/proto"
	"servers/internal/net_msg"
)

type GameClient interface {
	Send(serID uint32, pbMsg proto.Message) error
}

func NewGameConn(url string, recvCallBack func(net_msg.Message)) (*GameConn, error) {
	ctx := &GameConn{
		recvCallBack: recvCallBack,
		exitCh:       make(chan struct{}, 1),
	}

	var (
		err error
		msg []byte
	)

	if ctx.c, _, err = websocket.DefaultDialer.Dial(url, nil); err != nil {
		log.GetLogger().Error(err.Error()).Println()
		return nil, err
	}

	go func() {
		for {
			_, msg, err = ctx.c.ReadMessage()
			if err != nil {
				break
			}

			err = ctx.OnRecv(msg)
			if err != nil {
				break
			}
		}
		log.GetLogger().Warn("client logout").Println()
		ctx.exitCh <- struct{}{}
	}()
	return ctx, nil
}

const (
	packSize = 8096
)

type GameConn struct {
	c           *websocket.Conn
	isConnected atomic.Bool
	wg          sync.WaitGroup

	recvCallBack func(net_msg.Message)
	exitCh       chan struct{}

	sendCount  uint64
	recvCount  uint64
	sendRound  byte   // 字节映射
	recvRound  byte   // 字节映射
	sendXorKey uint32 // 发送密钥
	recvXorKey uint32 // 接收密钥

	recvBuffer [packSize]byte
	sendBuffer [packSize]byte
}

func (r *GameConn) Send(serID uint32, pbMsg proto.Message) (err error) {
	pbName := string(pbMsg.ProtoReflect().Descriptor().Name())
	pbBody, _ := proto.Marshal(pbMsg)
	msg := net_msg.TakeMessage()
	msg.SetMsg(0, str_conv.ToString(serID), pbName, pbBody)
	msgSend := msg.Marshal()
	err = r.c.WriteMessage(websocket.TextMessage, msgSend)
	if err == nil {
		return nil
	}
	if !errors.Is(err, e3net.ErrorConnClosed) {
		log.GetLogger().Warn(err.Error()).Println()
	}
	return e3net.ErrorConnClosed
}

func (r *GameConn) OnRecv(body []byte) (err error) {
	msg := net_msg.TakeMessage()
	if err = msg.Unmarshal(body); err != nil {
		log.GetLogger().ErrorPrintf("on recv msg Unmarshal err:%v", err)
		return err
	}
	// bb := msg.GetBody()
	// logger.DebugPrintf("msg[%v] len:%v  body len:%v body:%v", msg.GetName(), len(body), len(bb), bb)
	r.recvCallBack(msg)
	return nil
}

func (r *GameConn) OnInit(serviceName string, c *websocket.Conn) {
}

func (r *GameConn) OnClosed() {
	r.isConnected.Store(false)
	<-r.exitCh
}

func (r *GameConn) Close() {
	r.c.Close()
}
